#include "kmod.h"

/**
 * option:
 * 	true: 打开回显
 * 	false: 关闭回显
 */
static void set_disp_mode(int fd, bool option);
/**
 * 给出提示获取用户名
 */
static bool get_uname(char *cur_uname,char *uname, int count);
/**
 * 关闭回显，md5得到密码
 */
static bool get_passwd(char *passwd, int count);
//获取当前路径的绝对路径
static void get_cur_abs_path(char *abspath);
//分解命令得到数值
static int get_cmd_by_name(char *cstr);
//检查命令权限
bool is_allowed_priv(int cmd);
//加载用户权限
void load_user_privs();
//获取文件的绝对路径
void get_file_abs_path(char *filepath, char *fabspath);

//按下Ctrl+C后终止程序处理函数
static void key_control_c(int signal){
	if(signal == SIGINT){
		puts("\nCtrl+C Pressed. Program Terminated.");

		client_resource_revoke();
		exit(4);
	}
}
//请求退出
static void quit_request();
/**
 * 0 退出
 * -3 命令字过长
 * -2 命令为空
 * -1 错误指令
 * >1 命令数组
 */
int cmd_parse(char *cmd_str) {
	//直接回车
	if (strlen(cmd_str) == 1)
		return CMD_ENTER;
	//指令过长
	if(strlen(cmd_str) >= (STR_BUF_SZ-CMD_LEN-STATE_LEN))
		return CMD_TWO_LONG;
	//解析出命令
	char *p;
	sscanf(cmd_str, "%s", p);

	if (!strcmp("ls", p)) {
		return CMD_LS;
	} else if (!strcmp("cd", p)) {
		return CMD_CD;
	} else if (!strcmp("get", p)) {
		return CMD_GET;
	} else if (!strcmp("put", p)) {
		return CMD_PUT;
	} else if (!strcmp("pwd", p)) {
		return CMD_PWD;
	} else if (!strcmp("help", p)) {
		return CMD_HELP;
	} else if (!strcmp("bye", p)) {
		return CMD_BYE;
	} else if (p[0] == 33) {//"!"本地命令
		return CMD_LOCAL;
	} else {
		return CMD_NOT_FOUND;
	}
}

void cmd_rebuild(int cmd, int state, char *str){
	char buf[STR_BUF_SZ];

	strcpy(buf, str);
	buf[strlen(buf)-1] = 0;
	sprintf(str, "%-4d%-4d%s", cmd, state, buf);
}

void shell_exec(char *cmd, char *result) {
	FILE *fp;

	if ((fp = popen(cmd, "r")) != NULL) {
		fread(result, 1024, 1, fp);
		result[strlen(result) - 1] = 0;//去\n
		pclose(fp);
	}
}

//终止客户端
void key_control_c_register(){
	struct sigaction key_ctrl_c_act;
	key_ctrl_c_act.sa_handler = key_control_c;
	sigemptyset(&key_ctrl_c_act.sa_mask);
	key_ctrl_c_act.sa_flags = 0;
	sigaction(SIGINT, &key_ctrl_c_act, 0);
}

//传送服务器端套接字，将用户名及密码进行身份验证
bool access_auth(){
	char buf[AUTH_INFO_LEN];
	char uname[UNAME_LEN+2]={0}, cur_uname[UNAME_LEN]={0};
	char passwd[PASSWD_LEN+1];
	int len, state;

	bzero(&buf, STR_BUF_SZ);
	sprintf(buf, "%-4d%-4d", CMD_AUTH, STAT_CREADY);

	//获取当前用户名 并取得用户输入的用户名
	shell_exec("echo $USER", cur_uname);
	if(!get_uname(cur_uname, uname, sizeof(uname))){
		puts("Notice: ");
		printf("\tuserName should limited to %d characters.\n", UNAME_LEN);
		puts("\textra characters would be cut off.");
	}
	sprintf(buf+(CMD_LEN+STATE_LEN)*sizeof(char), "%-16s", uname);
	//获取用户输入的密码
	printf("PassWord: ");
	if(DEBUG){
		strcpy(passwd, "21232f297a57a5a743894a0e4a801fc3");
	}else{
		if(!get_passwd(passwd, sizeof(passwd))){
			return false;
		}
	}
	sprintf(buf+(CMD_LEN+STATE_LEN+UNAME_LEN)*sizeof(char), "%s", passwd);
	//发送到服务器端
	write(serv_sock, buf, STR_BUF_SZ);
	//清空数组
	memset(&buf, 0, STR_BUF_SZ);
	if((len=read(serv_sock, buf, STR_BUF_SZ)) <= 0){
		printf("Login failed.\n");
		return false;
	}
	sscanf(buf, "%4d%s", &state, privs);
	if(state == STAT_SREADY){
		printf("%d Login successful.\n", state);
		return true;
	}else{
		printf("%d Login failed.\n", state);
		return false;
	}
}

void client_resource_revoke(){
	if(authed){
		quit_request();
	}
	if(serv_sock != -1){
		close(serv_sock);
	}
}

void send_data(FILE *wfp, char *absfname){
	FILE *fp=fopen(absfname, "r");
	char data[STR_BUF_SZ]={0};
	int len;

	if(fp != NULL){
		while((len=fread(data, 1, STR_BUF_SZ, fp)) > 0){
			fwrite(data, 1, len, wfp);
			bzero(&data, STR_BUF_SZ);
			if(feof(fp) || ferror(fp) || len < STR_BUF_SZ){
				break;
			}
		}
		fflush(fp);
		fflush(wfp);
		fclose(fp);
	}
}

void recv_data(FILE *rfp, char *fname){
	FILE *fp=fopen(fname, "w");
	char data[STR_BUF_SZ]={0};
	int len;

	if(fp != NULL){
		while((len=fread(data, 1, STR_BUF_SZ, rfp)) > 0){
			fwrite(data, 1, len, fp);
			bzero(&data, STR_BUF_SZ);
			if(feof(rfp) || ferror(rfp) || len < STR_BUF_SZ){
				break;
			}
		}
		fflush(fp);
		fclose(fp);
	}
}

void load_user_privs(){
	int cmd;
	char swap[PRIV_CMD_LEN],*p;

	strcpy(swap, privs);
	if((p=strtok(swap, PRIV_DELIM)) != NULL){
		cmd=get_cmd_by_name(p);
		if(cmd != CMD_NOT_FOUND){
			uprivs[cmd-1] = 1;
		}
		while((p=strtok(NULL, PRIV_DELIM)) != NULL){
			cmd=get_cmd_by_name(p);
			if(cmd != CMD_NOT_FOUND){
				uprivs[cmd-1] = 1;
			}
		}
		//输出权限检查结果
		if(DEBUG){
			int i;

			for(i=0; i<PRIV_NUM; i++){
				printf("%d => %d \n", i, uprivs[i]);
			}
		}
	}
}

bool server_alive_query(){
	char buf[STR_BUF_SZ];
	int len, state;
	sprintf(buf, "%-4d%-4d", CMD_CONN, STAT_ALIVE);
	write(serv_sock, buf, STR_BUF_SZ);

	//清空数组
	bzero(&buf, STR_BUF_SZ);
	if((len=read(serv_sock, buf, STR_BUF_SZ)) <= 0){
		return false;
	}
	sscanf(buf, "%4d", &state);
	if(state == STAT_ALIVE){
		return true;
	}
	return false;
}

bool is_allowed_priv(int cmd){
	return (uprivs[cmd-1] == 1 ? true : false);
}

void get_file_abs_path(char *filepath, char *fabspath){
	if(filepath[0] == 47){//以“/”开头
		strcpy(fabspath, filepath);
	}else{
		get_cur_abs_path(fabspath);
		strcat(strcat(fabspath, "/"), filepath);
	}
}

void get_file_rel_name(char *fabspath, char *filename){
	char *p;

	p=strrchr(fabspath, '/');
	p++;
	strcpy(filename, p);
}

bool is_domain_name(char *str){
	char *p=str;

	while(*p++){
		if(isalpha(*p)){
			return true;
		}
	}

	return false;
}

static int get_cmd_by_name(char *cstr){
	if(!strcmp("ls", cstr)){
		return  CMD_LS;
	}else if(!strcmp("cd", cstr)){
		return CMD_CD;
	}else if(!strcmp("get", cstr)){
		return CMD_GET;
	}else if(!strcmp("put", cstr)){
		return CMD_PUT;
	}else if(!strcmp("pwd", cstr)){
		return CMD_PWD;
	}else if(!strcmp("help", cstr)){
		return CMD_HELP;
	}else if(!strcmp("bye", cstr)){
		return CMD_BYE;
	}else{
		return CMD_NOT_FOUND;
	}
}

static void get_cur_abs_path(char *abspath){
	shell_exec("pwd", abspath);
}

static void quit_request(){
	char buf[STR_BUF_SZ];
	sprintf(buf, "%-4d%-4d", CMD_BYE, STAT_CSTOP);
	write(serv_sock, buf, STR_BUF_SZ);
}

static void set_disp_mode(int fd, bool option) {
	struct termios term;

	if (tcgetattr(fd, &term) == -1) {
		perror("Cannot get the attribution of the terminal");
	}
	if (option){
		term.c_lflag |= ECHOFLAGS;
	}else{
		term.c_lflag &= ~ECHOFLAGS;
	}

	tcsetattr(fd, TCSAFLUSH, &term);
}

static bool get_uname(char *cur_uname,char *uname, int count){
	int str_len;

	printf("UserName (%s): ", cur_uname);
	bzero(uname, count);
	fgets(uname, count, stdin);
	str_len=strlen(uname);

	if(str_len == 1){//直接回车
		strcpy(uname, cur_uname);
		return true;
	}

	if(uname[str_len-1] != 10){
		uname[str_len] = 0;
		while(getchar() != 10)//读掉stdin的多余字符及换行
				;
		return false;
	}
	uname[str_len-1] = 0;//去换行

	return true;
}

static bool get_passwd(char *passwd, int count){
	char rawpasswd[STR_BUF_SZ];
	unsigned char encpasswd[16];
	MD5_CTX md5;

	set_disp_mode(STDIN_FILENO, false);
	bzero(&rawpasswd, STR_BUF_SZ);
	fgets(rawpasswd, STR_BUF_SZ, stdin);
	//md5检验
	md5_init(&md5);
	md5_update(&md5, rawpasswd,strlen((char *)rawpasswd)-1);
	md5_final(&md5, encpasswd);
	bzero(passwd, count);
	sprintf(passwd, "%02x%02x%02x%02x%02x%02x%02x%02x"
			"%02x%02x%02x%02x%02x%02x%02x%02x",
			encpasswd[0], encpasswd[1], encpasswd[2], encpasswd[3],
			encpasswd[4], encpasswd[5], encpasswd[6], encpasswd[7],
			encpasswd[8], encpasswd[9], encpasswd[10], encpasswd[11],
			encpasswd[12], encpasswd[13], encpasswd[14], encpasswd[15]);
	set_disp_mode(STDIN_FILENO, true);
	fputc('\n', stdout);

	return true;
}
